之前一个做Java后端的朋友问了我这个问题:“你们iOS中一段文字中怎么识别中间有没有URL”,我第一反应就是使用正则表达式,然后自己捣鼓了下就有了下面的结果。
由于自己对正则表达式不是特别懂,于是在谷歌上直接搜索到了一段,经过测试稍微修改下:
((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,7})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,7})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)
然后使用NSRegularExpression
来做正则校验
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regulaStr options:NSRegularExpressionCaseInsensitive error:&error];
其中options:(NSRegularExpressionOptions)
参数对应的枚举值的翻译是:
NSRegularExpressionCaseInsensitive // 不区分字母大小写匹配
NSRegularExpressionAllowCommentsAndWhitespace // 忽略正则表达式中的空格和#号后面的注释。 表达式 a b#c 会匹配到 ab
NSRegularExpressionIgnoreMetacharacters //将整个正则表达式视为字符串处理
NSRegularExpressionDotMatchesLineSeparators //允许.符号匹配任何字符包括换行符
NSRegularExpressionAnchorsMatchLines // 允许^和$符号匹配行的开头和结尾
NSRegularExpressionUseUnixLineSeparators // 只允许 \n 作为唯一的换行符(否则所有的标准换行符都有效)
NSRegularExpressionUseUnicodeWordBoundaries // 使用Unicode TR#29标准作为词的边界,否则所有传统正则表达式的词边界都有效
使用下面的方法根据正则表达式查询字符串中所有匹配的结果
- (NSArray<NSTextCheckingResult *> *)matchesInString:(NSString *)string options:(NSMatchingOptions)options range:(NSRange)range
其中options:(NSMatchingOptions)
参数对应的枚举值翻译如下:
NSMatchingReportProgress //找到最长的匹配字符串后调用block回调
NSMatchingReportCompletion // 找到任何一个匹配串后都回调一次block
NSMatchingAnchored // 从匹配范围的开始处进行匹配
NSMatchingWithTransparentBounds //允许匹配范围超出设置的范围
NSMatchingWithoutAnchoringBounds // 指定^和$不会自动匹配搜索范围的开始和结束
返回的类型是一个数组,数组中是一个NSTextCheckingResult
对象,可以用对象的属性range
来获取字符串中对应的url字符串。下面是源码:
NSString *testText = @"你看看我给你的连接http://blog.xtxiete.com和https://www.baidu.com/";
UILabel *testLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 100, 400, 100)];
[self.view addSubview:testLabel];
testLabel.numberOfLines = 0;
NSMutableAttributedString *text = [[NSMutableAttributedString alloc]initWithString:testText];
[text addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:17] range:NSMakeRange(0, testText.length)];
NSError *error;
//可以识别url的正则表达式
NSString *regulaStr = @"((http[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,7})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,7})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)";
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regulaStr options:NSRegularExpressionCaseInsensitive error:&error];
NSArray *arrayOfAllMatches = [regex matchesInString:testText options:NSMatchingReportProgress range:NSMakeRange(0, testText.length)];
for (NSTextCheckingResult *match in arrayOfAllMatches)
{
NSString* substringForMatch;
substringForMatch = [testText substringWithRange:match.range];
NSLog(@"url: %@",substringForMatch);
[text addAttribute:NSForegroundColorAttributeName value:[UIColor orangeColor] range:match.range];
}
testLabel.attributedText = text;
代码运行的效果图:
另外🍎有个自带的类也可以实现:NSDataDetector
+ (nullable NSDataDetector *)dataDetectorWithTypes:(NSTextCheckingTypes)checkingTypes error:(NSError **)error;
- (nullable instancetype)initWithTypes:(NSTextCheckingTypes)checkingTypes error:(NSError **)error
NSDataDetector
其实就是继承了NSRegularExpression
类,它不仅可以识别url
,还可以识别其他类型,具体可以查看枚举值
typedef NS_OPTIONS(uint64_t, NSTextCheckingType) { // a single type
NSTextCheckingTypeOrthography = 1ULL << 0, // language identification
NSTextCheckingTypeSpelling = 1ULL << 1, // spell checking
NSTextCheckingTypeGrammar = 1ULL << 2, // grammar checking
NSTextCheckingTypeDate = 1ULL << 3, // date/time detection
NSTextCheckingTypeAddress = 1ULL << 4, // address detection
NSTextCheckingTypeLink = 1ULL << 5, // link detection
NSTextCheckingTypeQuote = 1ULL << 6, // smart quotes
NSTextCheckingTypeDash = 1ULL << 7, // smart dashes
NSTextCheckingTypeReplacement = 1ULL << 8, // fixed replacements, such as copyright symbol for (c)
NSTextCheckingTypeCorrection = 1ULL << 9, // autocorrection
NSTextCheckingTypeRegularExpression API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0)) = 1ULL << 10, // regular expression matches
NSTextCheckingTypePhoneNumber API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0)) = 1ULL << 11, // phone number detection
NSTextCheckingTypeTransitInformation API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0)) = 1ULL << 12 // transit (e.g. flight) info detection
};
但是在某些情况下识别效果不是很好,比如
http://blog.xtxiete.com和https://www.baidu.com/
类似这种多个url
中间有中文的情况下,苹果的API识别出来的就是这一整个url
源码如下:
NSString *testText = @"你看看我给你的连接http://blog.xtxiete.com和https://www.baidu.com/";
UILabel *testLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 200, 400, 100)];
[self.view addSubview:testLabel];
testLabel.numberOfLines = 0;
NSMutableAttributedString *text = [[NSMutableAttributedString alloc]initWithString:testText];
[text addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:17] range:NSMakeRange(0, testText.length)];
NSError *error;
NSDataDetector *dataDetector=[NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink error:&error];
NSArray *arrayOfAllMatches=[dataDetector matchesInString:testText options:NSMatchingReportProgress range:NSMakeRange(0, testText.length)];
for (NSTextCheckingResult *match in arrayOfAllMatches)
{
NSString* substringForMatch;
substringForMatch = [testText substringWithRange:match.range];
NSLog(@"url: %@",substringForMatch);
[text addAttribute:NSForegroundColorAttributeName value:[UIColor orangeColor] range:match.range];
}
testLabel.attributedText = text;
查看下效果: